import {_copy, _instance} from "./Utils";
import {KeyRange, CursorDirection, TransactionMode} from "./Const";
import TableIndex from "./TableIndex";
import Database from "./Database";

const _getObjectStore: any = (table: Table, mode:TransactionMode = TransactionMode.READ_ONLY) => {
    const connect = table.database.connect;
    const trans = connect.transaction(table.name, mode);
    trans.oncomplete = (e: any) => console.debug(e);
    trans.onerror = (e: any) => console.debug(e);
    return trans.objectStore(table.name);
};

const _request = (request: any, success?: any): any => {
    return new Promise<any>((resolve, reject) => {
        request.onsuccess = (e: any) => {
            !success ? resolve(e.target.result) : success(e.target.result, resolve)
        };
        request.onerror = (e: any) => {
            reject(e.target.error)
        }
    })
};

export default class Table {
    readonly name : string;
    readonly primaryKey: string = "id";
    readonly autoIncrement: boolean = false;
    readonly indexes: Array<TableIndex> = [];
    readonly database: Database;
    /**
     *
     * @param name 表名
     * @param primaryKey 主键字段
     * @param autoIncrement 是否自动增长
     * @param indexes 表索引 @see TableIndex
     * @param data 表初始化数据
     * @param database 数据库对象 @see Database
     */
    constructor ({name = '', primaryKey = 'id', autoIncrement = false, indexes = [], data = []}, database: Database) {
        this.name = name;
        this.primaryKey = primaryKey;
        this.autoIncrement = !!autoIncrement;
        this.database = database;
        if(!database.exists(name)) {
            const store = database.connect.createObjectStore(name, {keyPath: this.primaryKey, autoIncrement})
            indexes.forEach(({name, column, unique, multiEntry}) => store.createIndex(name, column, {unique, multiEntry}))
            data.forEach(item => store.add(item))
        }
    }

    /**
     *
     * 插入数据 对象或者数据数组
     * @param data obj or arr
     */
    insert (data:Array<any>) {
        const arr = _instance(data, Array) ? data : [data];
        const store = _getObjectStore(this, TransactionMode.READ_WRITE);
        return new Promise((resolve) => {
            let results:any = [];
            arr.forEach((item, index) => {
                _request(store.add(item)).then((rs:any) => {
                    results.push(rs);
                    if (index + 1 === arr.length) resolve(results);
                });
            })
        })
    }

    /**
     * 当where未指定 直接更新modify
     * @param modify 更新内容
     * @param where 条件
     */
    update (modify:any, where?:any) {
        const store = _getObjectStore(this, TransactionMode.READ_WRITE)
        if(!where) {
            _request(store.put(modify));
        } else {
            return new Promise((resolve, reject) => {
                _request(store.getAll(where)).then((rs:any) => {
                    rs = _instance(rs, Array) ? rs : [rs];
                    let rows:any = [];
                    rs.forEach((item: any, index: number) => {
                        _request(store.put(_copy(modify, item))).then((r: any) => {
                            rows.push(r);
                            if (index + 1 === rs.length) {
                                resolve(rows);
                            }
                        });
                    });
                }).catch((err: any) => reject(err));
            })
        }
    }

    /**
     * 根据主键值删除
     * @param key
     */
    delete (key:any) {
        _request(_getObjectStore(this, TransactionMode.READ_WRITE).delete(key))
    }

    /**
     * 清空表
     */
    clear () {
        _request(_getObjectStore(this, TransactionMode.READ_WRITE).clear())
    }

    /**
     * 根据索引名称删除索引
     * @param name
     */
    dropIndex (name:string) {
        _getObjectStore(this, TransactionMode.READ_WRITE).deleteIndex(name)
    }

    /**
     * 获取指定范围内的主键列表
     * @param keyRange @link keyRange
     * @param limit
     */
    keys (keyRange?:KeyRange, limit?:number) {
        const store = _getObjectStore(this);
        const request: any = store.getAllKeys(keyRange, limit);
        return _request(request);
    }

    /**
     * 统计数量
     * @param keyRange @link keyRange
     */
    count(keyRange?:KeyRange) {
        const request = _getObjectStore(this).count(keyRange);
        return _request(request)
    }

    /**
     *
     * @param keyRange A value that is or resolves to an KeyRange.
     * @param limit Specifies the number of values to return if more than one is found.
     * @returns {*}
     */
    query(keyRange?:KeyRange, limit?:number) {
        const request = _getObjectStore(this).getAll(keyRange, limit);
        return _request(request)
    }

    queryByKey(key:any) {
        const request = _getObjectStore(this).get(key);
        return _request(request)
    }

    /**
     *
     * @param each
     * @param keyRange @see KeyRange
     * @param direction @see CursorDirection
     */
    fetch (keyRange?:KeyRange, direction?:CursorDirection) {
        const store = _getObjectStore(this);
        const request = store.openCursor(null, direction);
        return new Promise((resolve, reject) => {
            let rows: any = [];
            _request(request, (cursor: any) => {
                if (!cursor) {
                    resolve(rows);
                    return;
                }
                rows.push(cursor.value);
                cursor.continue();
            }).catch((err:any) => {
                reject(err);
            });
        });
    }

    /**
     *
     * @param name indexName
     * @param key key or keyRange
     */
    queryByIndex(name:string, key:any) {
        const store = _getObjectStore(this);
        const index = store.index(name);
        return _request(index.getAll(key))
    }

    /**
     * 获取的同时删除 消费掉表内的数据
     * direction: @see CURSOR_DIRECTION
     */
    deplete(direction: any = CursorDirection.NEXT) {
        const store = _getObjectStore(this, TransactionMode.READ_WRITE);
        const request = store.openCursor(null, direction);
        return new Promise((resolve, reject) => {
            let rows: any = [];
            _request(request, (cursor: any) => {
                if (!cursor) {
                    resolve(rows);
                    return;
                }
                rows.push(cursor.value);
                cursor.continue();
                store.delete(cursor.key);
            }).catch((err:any) => {
                reject(err);
            });
        });
    }

    /**
     * use index and cursor
     */
    multiple (indexName:string, each:Function, keyRange?:KeyRange, cursorDirection?:CursorDirection) {
        const store = _getObjectStore(this)
        const index = store.index(indexName)
        const request = index.openCursor(keyRange, cursorDirection || CursorDirection.NEXT);
        return new Promise((resolve, reject) => {
            let rows: any = [];
            _request(request, (cursor:any) => {
                if(!cursor) {
                    resolve(rows);
                }
                rows.push(cursor.value);
                cursor.continue()
            }).catch((err: any) => {
                reject(err)
            })
        })
    }
}
